/*
 * Solution.cpp
 *
 *  Created on: Feb 23, 2011
 *      Author: santiago
 */

#include <iostream>
#include <stdio.h>
#include <assert.h>

#include "Solution.h"
#include "TSPCostMatrix.h"
#include "TSPPath.h"

Solution::Solution() : _solution_cost(0.0), _cost_matrix(NULL) {
}

Solution::Solution(TSPCostMatrix *cost_matrix) : _solution_cost(0.0), _cost_matrix(cost_matrix) {
	_links.reserve(get_costMatrix()->get_node_count());

	vector<bool> *node_links;
	for (unsigned int nodeIndex = 0; nodeIndex < get_costMatrix()->get_node_count(); nodeIndex++) {
		node_links = new vector<bool> ();
		node_links->reserve(nodeIndex + 1);

		for (unsigned int destiantionNodeIndex = 0; destiantionNodeIndex
				< nodeIndex + 1; destiantionNodeIndex++) {

			node_links->push_back(false);
		}

		_links.push_back(*node_links);
		delete node_links;
	}
}

Solution::Solution(TSPCostMatrix &cost_matrix) : _solution_cost(0.0), _cost_matrix(&cost_matrix) {
	_links.reserve(get_costMatrix()->get_node_count());

	vector<bool> *node_links;
	for (unsigned int nodeIndex = 0; nodeIndex < get_costMatrix()->get_node_count(); nodeIndex++) {
		node_links = new vector<bool> ();
		node_links->reserve(nodeIndex + 1);

		for (unsigned int destiantionNodeIndex = 0; destiantionNodeIndex
				< nodeIndex + 1; destiantionNodeIndex++) {

			node_links->push_back(false);
		}

		_links.push_back(*node_links);
		delete node_links;
	}
}

/*
 * Crea una copia de una solución.
 */
Solution::Solution(const Solution &original) : _solution_cost(original._solution_cost), _cost_matrix(original._cost_matrix) {
	_links.reserve(get_costMatrix()->get_node_count());

	vector<bool> *node_links;
	for (unsigned int nodeIndex = 0; nodeIndex < get_costMatrix()->get_node_count(); nodeIndex++) {
		node_links = new vector<bool> ();
		node_links->reserve(nodeIndex + 1);

		for (unsigned int destiantionNodeIndex = 0; destiantionNodeIndex
				< nodeIndex + 1; destiantionNodeIndex++) {

			node_links->push_back(original.has_link(nodeIndex,
					destiantionNodeIndex));
		}

		_links.push_back(*node_links);
		delete node_links;
	}
}

Solution::~Solution() {

}

Solution& Solution::operator=(const Solution &original) {
	_cost_matrix = original._cost_matrix;
	_solution_cost = original.get_cost();
	_links.reserve(get_costMatrix()->get_node_count());

	vector<bool> *node_links;
	for (unsigned int nodeIndex = 0; nodeIndex < get_costMatrix()->get_node_count(); nodeIndex++) {
		node_links = new vector<bool> ();
		node_links->reserve(nodeIndex + 1);

		for (unsigned int destiantionNodeIndex = 0; destiantionNodeIndex
				< nodeIndex + 1; destiantionNodeIndex++) {

			node_links->push_back(original.has_link(nodeIndex,
					destiantionNodeIndex));
		}

		_links.push_back(*node_links);
		delete node_links;
	}

	return *this;
}

void Solution::convert_to_matrix(unsigned int node_index_1,
		unsigned int node_index_2, unsigned int &first_node_index,
		unsigned int &second_node_index) const {
	if (node_index_1 < node_index_2) {
		first_node_index = node_index_2;
		second_node_index = node_index_1;
	} else {
		first_node_index = node_index_1;
		second_node_index = node_index_2;
	}

	assert(_links.size() > first_node_index);
	assert(_links[first_node_index].size() > second_node_index);
}

/*
 * Agrega el enlace entre ambos nodos a la solución.
 */
void Solution::add_link(unsigned int node_index_1, unsigned int node_index_2) {
	assert(!has_link(node_index_1, node_index_2));

	unsigned int first_node_index;
	unsigned int second_node_index;

	convert_to_matrix(node_index_1, node_index_2, first_node_index,
			second_node_index);

	_links[first_node_index][second_node_index] = true;
	_solution_cost = _solution_cost + get_costMatrix()->get_cost(node_index_1,
			node_index_2);
}

/*
 * Elimina el enlace entre los nodos de la solución.
 */
void Solution::del_link(unsigned int node_index_1, unsigned int node_index_2) {
	assert(has_link(node_index_1, node_index_2));

	unsigned int first_node_index;
	unsigned int second_node_index;

	convert_to_matrix(node_index_1, node_index_2, first_node_index,
			second_node_index);

	_links[first_node_index][second_node_index] = false;
	_solution_cost = _solution_cost - get_costMatrix()->get_cost(node_index_1,
			node_index_2);
}

/*
 * Retorna TRUE si existe un enlace entre ambos nodos.
 */
bool Solution::has_link(unsigned int node_index_1, unsigned int node_index_2) const {
	unsigned int first_node_index;
	unsigned int second_node_index;

	convert_to_matrix(node_index_1, node_index_2, first_node_index,
			second_node_index);

	return _links[first_node_index][second_node_index];
}

/*
 * Obtiene todos los nodos que se encuentran enlazados con el nodo especificado.
 */
void Solution::get_links(unsigned int node_index,
		vector<unsigned int> &node_links) const {
	for (unsigned int prev_node = 0; prev_node < node_index; prev_node++) {
		if (_links[node_index][prev_node]) {
			node_links.push_back(prev_node);
		}
	}

	for (unsigned int succ_node = node_index + 1; succ_node
			< get_costMatrix()->get_node_count(); succ_node++) {
		if (_links[succ_node][node_index]) {
			node_links.push_back(succ_node);
		}
	}
}

unsigned int Solution::get_links_count(unsigned int node_index) const {
	unsigned int node_count = 0;

	for (unsigned int prev_node = 0; prev_node < node_index; prev_node++) {
		if (_links[node_index][prev_node]) {
			node_count++;
		}
	}

	for (unsigned int succ_node = node_index + 1; succ_node
			< get_costMatrix()->get_node_count(); succ_node++) {
		if (_links[succ_node][node_index]) {
			node_count++;
		}
	}

	return node_count;
}

/*
 * Verifica la factibilidad de la solución.
 * TODO: implementar! (siempre retorna TRUE)
 */
bool Solution::check_factibility() const {
	return true;
}

/*
 * Imprime a pantalla los enlaces de la solución.
 */
void Solution::show() const {
	puts("=== LINK MATRIX ======================");
	for (unsigned int current_node_Id = 0; current_node_Id < _links.size(); current_node_Id++) {
		for (unsigned int destination_node_Id = 0; destination_node_Id
				< current_node_Id; destination_node_Id++) {

			if (has_link(current_node_Id, destination_node_Id)) {
				cout << "(" << current_node_Id << "," << destination_node_Id
						<< ")" << endl;
			}
		}
	}

	cout << "Nodos de grado 0: " ;
	for (unsigned int current_node_Id = 0; current_node_Id < _links.size(); current_node_Id++) {
		if (get_links_count(current_node_Id) == 0) {
			cout << current_node_Id << " ";
		}
	}
	cout << endl;


	cout << endl << "TOTAL SOLUTION COST: " << get_cost() << endl;
	puts("======================================");
}

double Solution::get_cost_to_add(const TSPPath &p) const {
	assert(p.check_path());
	double cost = 0.0;

	for (unsigned int i = 0; i < p.get_size() - 1; i++) {
		if (!has_link(p.get_node_at(i), p.get_node_at(i+1))) {
			cost += get_costMatrix()->get_cost(p.get_node_at(i), p.get_node_at(i+1));
		}
	}

	return cost;
}

void Solution::del_path(const TSPPath &p) {
	for (unsigned int i = 0; i < p.get_size() - 1; i++) {
		if (has_link(p.get_node_at(i), p.get_node_at(i+1))) {
			del_link(p.get_node_at(i), p.get_node_at(i+1));
		}
	}
}

void Solution::add_path(const TSPPath &p) {
	assert(p.check_path());

	for (unsigned int i = 0; i < p.get_size() - 1; i++) {
		if (!has_link(p.get_node_at(i), p.get_node_at(i+1))) {
			add_link(p.get_node_at(i), p.get_node_at(i+1));
		}
	}
}

/*
 * Retorna el costo total de la solución.
 */
double Solution::get_cost() const {
	return _solution_cost;
}

TSPCostMatrix* Solution::get_costMatrix() const {
	return _cost_matrix;
}
